#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/resource.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <math.h>

typedef unsigned int RING_IDX;
#define SPIN_LOCK_UNLOCKED 0
typedef int spinlock_t;

#define MAX_SECTOR_SIZE 512
#define SRINGSIZE 4096*32
//#define SRINGSIZE 4096
#define PAGE_SIZE SRINGSIZE

typedef struct disk_event_st {
	unsigned int processed;//0: request, 1: response
	unsigned int domain_pid;
	unsigned int domain_tid;
	unsigned int sector_number;
	unsigned int shm_id;
	//unsigned long int sector[MAX_SECTOR_SIZE/sizeof(unsigned long int)];
} disk_event_request_t, disk_event_response_t;

/* Shared ring entry */
union __disk_sring_entry {
    disk_event_request_t req;
    disk_event_response_t rsp;
};

/* Shared ring page */
struct __disk_sring {
	RING_IDX req_prod, req_event;
	RING_IDX rsp_prod, rsp_event;
	unsigned int domain_pid;
	unsigned int shm_id;
	unsigned int msg_id;
	pthread_mutex_t ring_lock;
	pthread_cond_t ring_cond;
	pthread_cond_t ring_have_request;
	pthread_cond_t ring_have_response;
	unsigned int status;
	unsigned int full;
	/*union {
		struct {
			uint8_t smartpoll_active;
		} netif;
        	struct {
			uint8_t msg;
		} tapif_user;
        		uint8_t pvt_pad[4];
		} private;
	uint8_t __pad[44];*/
	union __disk_sring_entry ring[1]; /* variable-length */
};

/* "Front" end's private variables */
struct __disk_front_ring {
    RING_IDX req_prod_pvt;
    RING_IDX rsp_cons;
    unsigned int nr_ents; /* RING_SIZE */
    struct __disk_sring *sring;
};

/* "Back" end's private variables */
struct __disk_back_ring {
    RING_IDX rsp_prod_pvt;
    RING_IDX req_cons;
    unsigned int nr_ents; /* RING_SIZE */
    struct __disk_sring *sring;
};

/* Syntactic sugar */
typedef struct __disk_sring __disk_sring_t;
typedef struct __disk_front_ring __disk_front_ring_t;
typedef struct __disk_back_ring __disk_back_ring_t;


typedef struct __domain {
	unsigned int domain_pid;
	unsigned int shm_id;
	unsigned int msg_id;
	pthread_mutex_t ring_lock;
	pthread_cond_t ring_cond;
	__disk_front_ring_t *front_ring;
	__disk_back_ring_t *back_ring;
} domain;

void disk_event_ring_lock_init(__disk_sring_t *_s) {
	pthread_mutex_init(&(_s->ring_lock), NULL);
	pthread_cond_init(&(_s->ring_cond), NULL);
	pthread_cond_init(&(_s->ring_have_request), NULL);
	pthread_condattr_t ring_condattr;
	pthread_condattr_init(&ring_condattr);
      	pthread_condattr_setpshared(&ring_condattr, PTHREAD_PROCESS_SHARED);
	pthread_cond_init(&(_s->ring_have_response), &ring_condattr);
}

void disk_event_ring_lock(__disk_sring_t *_s) {
	if (pthread_mutex_lock(&(_s->ring_lock)))
		fprintf(stderr, "error in locking mutex\n");
}

void disk_event_ring_unlock(__disk_sring_t *_s) {
	if (pthread_mutex_unlock(&(_s->ring_lock)))
		fprintf(stderr, "error in unlocking mutex\n");
}

void disk_event_ring_signal(__disk_sring_t *_s) {
	if (pthread_cond_signal(&(_s->ring_cond))) /*signal condition variable ring_cond*/
		fprintf(stderr, "error in signaling condition variable\n");
}

void disk_event_ring_destroy(__disk_sring_t *_s) {
	pthread_cond_destroy(&(_s->ring_cond)); /* cleanup condition variable */
	pthread_cond_destroy(&(_s->ring_have_request)); /* cleanup condition variable */
	pthread_cond_destroy(&(_s->ring_have_response)); /* cleanup condition variable */
	pthread_mutex_destroy(&(_s->ring_lock)); /* cleanup lock */
	_s = NULL;
}

/*void disk_event_ring_lock_init(domain *d) {
	pthread_mutex_init(&(d->ring_lock), NULL);
	pthread_cond_init(&(d->ring_cond), NULL);
}

void disk_event_ring_lock(domain *d) {
	if (pthread_mutex_lock(&(d->ring_lock)))
		fprintf(stderr, "error in locking mutex\n");
	while (1)
		if (pthread_cond_wait(&(d->ring_cond), &(d->ring_lock)))
			fprintf(stderr, "error in waiting condition variable\n");

}

void disk_event_ring_unlock(domain *d) {
	if (pthread_mutex_unlock(&(d->ring_lock)))
		fprintf(stderr, "error in unlocking mutex\n");
}*/

/* Round a 32-bit unsigned constant down to the nearest power of two. */
#define __RD2(_x)  (((_x) & 0x00000002) ? 0x2                  : ((_x) & 0x1))
#define __RD4(_x)  (((_x) & 0x0000000c) ? __RD2((_x)>>2)<<2    : __RD2(_x))
#define __RD8(_x)  (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4    : __RD4(_x))
#define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8    : __RD8(_x))
#define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x))

/*
 * Calculate size of a shared ring, given the total available space for the
 * ring and indexes (_sz), and the name tag of the request/response structure.
 * A ring contains as many entries as will fit, rounded down to the nearest 
 * power of two (so we can mask with (size-1) to loop around).
 */
#define __RING_SIZE(_s, _sz) \
    (__RD32(((_sz) - (long)(_s)->ring + (long)(_s)) / sizeof((_s)->ring[0])))


/* Initialising empty rings */
void SHARED_RING_INIT(__disk_sring_t *_s) {
	(_s)->req_prod  = 0;
	(_s)->rsp_prod  = 0;
	(_s)->req_event = 1;
	(_s)->rsp_event = 1;
	(_s)->full = 0;
    //(void)memset((_s)->private.pvt_pad, 0, sizeof((_s)->private.pvt_pad)); /* write 0 into pvt_pad */
    //(void)memset((_s)->__pad, 0, sizeof((_s)->__pad));
}

void FRONT_RING_INIT(__disk_front_ring_t *_r, __disk_sring_t *_s, unsigned int __size) {
    (_r)->req_prod_pvt = 0;
    (_r)->rsp_cons = 0;
    (_r)->nr_ents = __RING_SIZE(_s, __size);//printf("front ring size:%d\n",(_r)->nr_ents);
    (_r)->sring = (_s);
}

void BACK_RING_INIT(__disk_back_ring_t *_r, __disk_sring_t *_s, unsigned int __size) {
    (_r)->rsp_prod_pvt = 0;
    (_r)->req_cons = 0;
    (_r)->nr_ents = __RING_SIZE(_s, __size);//printf("back ring size:%d\n",(_r)->nr_ents);
    (_r)->sring = (_s);
}

/* How big is this ring? */
#define RING_SIZE(_r)                                                   \
    ((_r)->nr_ents)

/* Check how many free positions on the ring */
unsigned int RING_FREE_REQUESTS(__disk_front_ring_t *_r) {
    return ((_r)->nr_ents - ((_r)->req_prod_pvt - (_r)->rsp_cons));
}

unsigned int RING_HAS_UNCONSUMED_REQUESTS(__disk_back_ring_t *_r) {
    return (_r)->sring->req_prod - (_r)->req_cons;
}

unsigned int RING_HAS_UNCONSUMED_RESPONSES(__disk_front_ring_t *_r) {
    return (_r)->sring->rsp_prod - (_r)->rsp_cons;
}

/* Direct access to individual ring elements, by index. */
disk_event_request_t *FRONTRING_GET_REQUEST(__disk_front_ring_t *_r, RING_IDX _idx) {
    return (&((_r)->sring->ring[((_idx) & ((_r)->nr_ents - 1))].req));
}

disk_event_request_t *BACKRING_GET_REQUEST(__disk_back_ring_t *_r, RING_IDX _idx) {
    return (&((_r)->sring->ring[((_idx) & ((_r)->nr_ents - 1))].req));
}

disk_event_response_t *FRONTRING_GET_RESPONSE(__disk_front_ring_t *_r, RING_IDX _idx) {
    return (&((_r)->sring->ring[((_idx) & ((_r)->nr_ents - 1))].rsp));
}

disk_event_response_t *BACKRING_GET_RESPONSE(__disk_back_ring_t *_r, RING_IDX _idx) {
    return (&((_r)->sring->ring[((_idx) & ((_r)->nr_ents - 1))].rsp));
}

int disk_event_put_request(__disk_front_ring_t *front_ring, disk_event_request_t *req) /* put request to ring */
{
	int status = 0;
    //__disk_front_ring_t *front_ring;
    RING_IDX req_prod;

	disk_event_ring_lock(front_ring->sring);

	if(RING_FREE_REQUESTS(front_ring) == 0) /* the ring is full */
	{
		/* don't block on full ring, return 1 */
		printf("ring is full\n");
		status = 1;
		front_ring->sring->full = 1;
	}
	else
	{
		//front_ring = d->front_ring;
		req_prod = front_ring->req_prod_pvt;

		/* Copy request */
		memcpy(FRONTRING_GET_REQUEST(front_ring, req_prod), req, sizeof(*req)); /* Put request on ring with index req_prod */
		req_prod++; /* Increment shared ring's request producer count */

		/* Update ring */
		front_ring->req_prod_pvt = req_prod; /* Update the private copy of request producer count in front ring */
		//RING_PUSH_REQUESTS(front_ring);
		front_ring->sring->req_prod = front_ring->req_prod_pvt;

		front_ring->sring->full = 0;
		//disk_event_ring_signal(d->front_ring->sring);
		//if (pthread_cond_signal(&(d->front_ring->sring->ring_have_request))) /*signal that there are requests */
		//	fprintf(stderr, "error in signaling condition variable\n");
	}

    //disk_event_ring_unlock(d);
	disk_event_ring_unlock(front_ring->sring);

    //notify_via_xen_event_channel(d, d->mem_event.xen_port);
	return status;
}

int disk_event_get_response(__disk_front_ring_t *front_ring, disk_event_response_t *rsp) /* Get response from ring */
{
    //__disk_front_ring_t *front_ring;
    RING_IDX rsp_cons;

	if(RING_HAS_UNCONSUMED_RESPONSES(front_ring) == 0) /* no responses on the ring, return -1 */
		return -1;
	//disk_event_ring_lock(front_ring->sring);
	//while(RING_HAS_UNCONSUMED_RESPONSES(front_ring) == 0) /* no responses on the ring */
	//	if (pthread_cond_wait(&(front_ring->sring->ring_have_response), &(front_ring->sring->ring_lock))) /*block on ring_have_response*/
	//		fprintf(stderr, "error in waiting ring_have_response\n");

    //front_ring = d->front_ring;
    rsp_cons = front_ring->rsp_cons;

    /* Copy response */
    memcpy(rsp, FRONTRING_GET_RESPONSE(front_ring, rsp_cons), sizeof(*rsp)); /* Get response from ring with index rsp_cons */
    rsp_cons++; /* Increment shared ring's response consumer count */

    /* Update ring */
    front_ring->rsp_cons = rsp_cons;
    front_ring->sring->rsp_event = rsp_cons + 1;

	//disk_event_ring_unlock(front_ring->sring);
	return 0;
}

int disk_event_get_request(__disk_back_ring_t *back_ring, disk_event_request_t *req) /* Get request from ring */
{
    //__disk_back_ring_t *back_ring;
    RING_IDX req_cons;

	if(RING_HAS_UNCONSUMED_REQUESTS(back_ring) == 0) /* no requests on the ring, return -1 */
		return -1;
	//disk_event_ring_lock(d->back_ring->sring);
	//while(RING_HAS_UNCONSUMED_REQUESTS(d->back_ring) == 0) /* no requests on the ring */
	//	if (pthread_cond_wait(&(d->back_ring->sring->ring_have_request), &(d->back_ring->sring->ring_lock))) /*block on variable queue_cv*/
	//		fprintf(stderr, "error in waiting condition variable\n");

    //back_ring = d->back_ring;
    req_cons = back_ring->req_cons;

    /* Copy request */
    memcpy(req, BACKRING_GET_REQUEST(back_ring, req_cons), sizeof(*req)); /* Get request from ring with index req_cons */
    req_cons++;

    /* Update ring */
    back_ring->req_cons = req_cons;
    back_ring->sring->req_event = req_cons + 1;

	//disk_event_ring_unlock(d->back_ring->sring);
	return 0;
}

void disk_event_put_response(__disk_back_ring_t *back_ring, disk_event_response_t *rsp) /* Put response to ring */
{
    //__disk_back_ring_t *back_ring;
    RING_IDX rsp_prod;

	//disk_event_ring_lock(d->back_ring->sring);

    //back_ring = d->back_ring;
    rsp_prod = back_ring->rsp_prod_pvt;

    /* Copy response */
    memcpy(BACKRING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp)); /* Put response on ring with index rsp_prod */
    rsp_prod++;

    /* Update ring */
    back_ring->rsp_prod_pvt = rsp_prod;
    //RING_PUSH_RESPONSES(back_ring);
	back_ring->sring->rsp_prod = back_ring->rsp_prod_pvt;
//printf("rsp_prod:%d\n", rsp_prod);
	//if (pthread_cond_signal(&(back_ring->sring->ring_have_response))) /*signal that there are responses */
	//		fprintf(stderr, "error in signaling ring_have_response\n");

	//disk_event_ring_unlock(d->back_ring->sring);
}
